import Vue from 'vue';
import $store from '@/store';
import CONFIG from '@/config';

export const $XReport = {
  data: {
    version: '2.0.0',
    system: {},
    appVersion: CONFIG.appVersion,
    appName: CONFIG.appName,
    logs: [] // 用户行为日志
  },
  config: {
    $http: uni.request,
    debug: false, // 调试模式
    api: null, // 接收api地址
    project: CONFIG.project || '99_mall', // 项目名称，必要参数
    pages: 'all', // 需要上报的页面路径 'all' || ['pages/index/index'] 页面路径列表
    exclude: ['pages/transit/index', 'pages/transit-product/index'], // 排除上报的页面
    opportunity: ['onHide', 'onUnload', 'onReport', 'onStart'], // 上报时机
    name: 'x-uni-log', // global全局数据缓存名称
    method: 'POST', // get / post
    header: null, // request header
    filterScene: [1129], // 根据场景值过滤
    systemKey: [
      'host',
      'safeArea',
      'model',
      'pixelRatio',
      'system',
      'language',
      'version',
      'SDKVersion',
      'brand',
      'platform',
      'fontSizeSetting',
      'statusBarHeight',
      'screenWidth',
      'screenHeight',
      'benchmarkLevel'
    ], // 需要上报的设备系统信息key
    dataType: 'json', // 上报数据格式，text文本, json对象
    success: null // 自定义数据上报函数
  },
  promisify(api) {
    return (options, ...params) => {
      return new Promise((resolve, reject) => {
        api(Object.assign({}, options, {
          success: resolve,
          fail(res) {
            console.error('request error ', res);
            reject(res);
          }
        }), ...params);
      }).catch(res => {
        console.log('Promise catch: ', res);
      });
    };
  },
  complete(api) {
    return (options, ...params) => {
      return new Promise((resolve, reject) => {
        api(Object.assign({}, options, { complete: resolve }), ...params);
      }).catch(res => {
        console.log('Promise catch: ', res);
      });
    };
  },
  async init(options = {}) {
    Object.assign(this.config, options);
    // console.log('XReport init', this.config);
    // onLaunch、onPageNotFound、onError拦截器
    $interceptor.init();
    // Page生命周期注入
    Vue.mixin(this.mixin);
    // 设备信息
    if (this.config.systemKey && this.config.systemKey.length) {
      this.setSystem();
    }
    // 网络环境
    const { networkType } = await this.complete(uni.getNetworkType)();
    this.data.networkType = networkType;
  },
  // 获取设备系统信息
  setSystem() {
    const system = uni.getSystemInfoSync();
    this.data.system = {};
    this.config.systemKey.map(v => {
      this.data.system[v] = system[v];
    });
  },
  mixin: {
    onLoad(options) {
      $XTrack('onLoad', options);
    },
    onShow(options) {
      $XTrack('onShow', options);

      // 获取服务器时间
      try {
        const pages = getCurrentPages();
        const page = pages[pages.length - 1];
        if (page && !$XReport.config.exclude.includes(page.route)) {
          setTimeout(() => {
            $store.dispatch('app/getTime').then(res => {
              $XTrack('serverTime', res.data);
            });
          }, 1000);
        }
      } catch (e) {
        console.warn(e);
      }
    },
    onHide(options) {
      $XTrack('onHide', options);
    },
    onUnload(options) {
      $XTrack('onUnload', options);
    }
  }
};

// $XTrack
export const $XTrack = (event, options = {}) => {
  $XReport.config.debug && console.log('x-uni-log event -> ', event, options);
  const { data, config } = $XReport;
  const pages = getCurrentPages();
  const page = pages[pages.length - 1];
  // onLaunch，App onShow
  if (event === 'onLaunch' || (event === 'onShow' && options.scene)) {
    Object.assign(data, {
      scene: options.scene // 场景值
    });
    $store.dispatch('app/setScene', options.scene);
  }
  // onLoad
  if (event === 'onLoad') {
    // history
    Object.assign(data, {
      history: pages.map(item => item.route),
      query: options,
      path: page.route,
      project: config.project,
      pageId: options.pageId,
      customerId: options?.storeId || null
    });
  }
  // onShow
  if (event === 'onShow' && data.logs.some(s => s.event === 'onShow')) {
    // 第二个onShow 定义为页面级别
    event = 'Page onShow';
  }
  // 行为记录
  if (!data.logs) {
    data.logs = [];
  }

  if (event === 'push') {
    // 向外层增加值
    Object.assign(data, options);
  } else if (event === 'logsMapIndex') {
    // 向外层增加logs map
    if (!data['logsMapIndex']) {
      data['logsMapIndex'] = {};
    }
    data['logsMapIndex'] = {
      ...data['logsMapIndex'],
      ...options
    };
  } else {
    data.logs.push({
      event,
      options,
      timestamp: +new Date()
    });
  }

  if (config.opportunity.some(s => s === event)) {
    // 特定的场景值不进行数据上报
    let isReport = !config.filterScene.some(s => s === data.scene);
    // 日志数据是否完成的
    if (isReport) {
      isReport = data.logs.some(s => s.event === 'onShow');
      isReport = data.logs.length > 2;
    }
    // 是否在需要上报的页面路径集中
    if (isReport && Object.prototype.toString.call(config.pages) === '[object Array]') {
      isReport = config.pages.some(s => {
        // 处理自定义配置路径集有 / 开头的
        const p = [data.path];
        if (/^\//.test(s)) {
          p.unshift('/');
        }
        return s === p.join('');
      });
    }
    // 过滤不上报的页面
    if (isReport) {
      isReport = !config.exclude.includes(data.path);
    }
    // 是否符合上报条件
    if (isReport) {
      const params = JSON.parse(JSON.stringify(data));
      // 获取openid
      const { openId, unionId } = uni.getStorageSync(CONFIG.userInfoName) || {};
      Object.assign(params, {
        unionId,
        openid: openId,
        systemInfo: data.system,
        accessToken: uni.getStorageSync(CONFIG.tokenName)
      });
      if (Object.prototype.toString.call(config.success) === '[object Function]') {
        config.success.call(this, params);
      } else if (config.api) {
        // 获取traceId
        try {
          const StorageTraceId = uni.getStorageSync('storage-trace-id');
          config.$http({
            timeout: 1000,
            url: '/account/user/iu/request/trace',
            report: false,
            data: {
              traceId: StorageTraceId || null
            }
          }).then(res => {
            params.traceId = res.data;
            uni.setStorageSync('storage-trace-id', res.data);
            const httpParams = {
              timeout: 2000,
              report: false,
              method: config.method,
              url: config.api,
              data: config.dataType === 'json' ? params : JSON.stringify(params)
            };

            config.$http(httpParams).catch(() => {
              // 失败了的话，重试一次
              config.$http(httpParams);
            });
          }).catch(() => {
            // 获取不到 traceId，也去上报日志
            const httpParams = {
              timeout: 2000,
              report: false,
              method: config.method,
              url: config.api,
              data: config.dataType === 'json' ? params : JSON.stringify(params)
            };
            config.$http(httpParams);
          });
        } catch (e) {
          console.warn('get trace id err', e);
        }
      } else {
        console.warn('请配置api或者success');
      }
    }
    // 上报成功，清除记录
    // 主动初始上报的，暂时不清除日志
    if (event !== 'onStart') {
      data.logs = [];
    }
  }
  // console.log('x-uni-log-> ', event, options, data)
};

// app
export const $interceptor = {
  init() {
    const oldApp = App;
    const _this = this;
    // eslint-disable-next-line no-native-reassign,no-global-assign
    App = function(Obj) {
      _this.interceptor(Obj, 'onLaunch');
      _this.interceptor(Obj, 'onPageNotFound');
      _this.interceptor(Obj, 'onError');
      oldApp(Obj);
    };
  },
  interceptor(Obj, event) {
    const e = Obj[event];
    Obj[event] = function(option) {
      // 自定义code
      // 日志上报
      $XTrack(event, option);
      // 原始方法调用
      e && e.call(this, option);
    };
  }
};
